﻿// <copyright file="EntityFrameworkStorageModelTemplate.cs" company="T4 Toolbox Team">
//  Copyright © T4 Toolbox Team. All Rights Reserved.
// </copyright>

namespace T4Toolbox.EntityFramework
{
    using System;
    using System.Collections.Generic;
    using System.Data.Entity.Design;
    using System.Data.Metadata.Edm;
    using System.IO;
    using System.Xml;

    /// <summary>
    /// Generates Entity Framework storage model from a given database.
    /// </summary>
    public class EntityFrameworkStorageModelTemplate : EntityFrameworkModelTemplate
    {
        /// <summary>
        /// Stores filter entries.
        /// </summary>
        private List<EntityStoreSchemaFilterEntry> filters;

        /// <summary>
        /// Stores storage model generated by this template.
        /// </summary>
        private string storageModel;

        /// <summary>
        /// Initializes a new instance of the <see cref="EntityFrameworkStorageModelTemplate"/> class.
        /// </summary>
        public EntityFrameworkStorageModelTemplate()
        {
            this.Provider = "System.Data.SqlClient";
            this.Namespace = TransformationContext.DefaultNamespace;
            this.filters = new List<EntityStoreSchemaFilterEntry>();
        }

        /// <summary>
        /// Gets or sets the name of ADO.NET data provider that will be used to connect 
        /// to the database.
        /// </summary>
        /// <value>
        /// A <see cref="String"/> that contains a valid ADO.NET data provider name,
        /// "System.Data.SqlClient" by default.
        /// </value>
        public string Provider { get; set; }

        /// <summary>
        /// Gets or sets the ADO.NET connection string that will be used to connect to 
        /// the database.
        /// </summary>
        /// <value>
        /// A <see cref="String"/> that contains a valid ADO.NET connection string. 
        /// </value>
        public string ConnectionString { get; set; }

        /// <summary>
        /// Gets a list of filter entries that determine tables, views and stored procedures that
        /// will be imported from the database.
        /// </summary>
        /// <value>
        /// A list of <see cref="EntityStoreSchemaFilterEntry"/> objects.
        /// </value>
        public ICollection<EntityStoreSchemaFilterEntry> Filters
        {
            get { return this.filters; }
        }

        /// <summary>
        /// Gets or sets the namespace for the storage model.
        /// </summary>
        /// <value>
        /// A <see cref="String"/> that contains a valid C# or Visual Basic namespace name.
        /// </value>
        public string Namespace { get; set; }

        /// <summary>
        /// Gets the Entity Framework storage model generated by this template.
        /// </summary>
        /// <value>
        /// A <see cref="String"/> that contains generated SSDL in XML format.
        /// </value>
        public string StorageModel
        {
            get { return this.storageModel; }
        }

        /// <summary>
        /// Generates Storage Model for the database in the specified <see cref="ConnectionString"/>.
        /// </summary>
        /// <returns>
        /// Generated SSDL in XML format.
        /// </returns>
        /// <remarks>
        /// Implementation note: We have to use a MemoryStream to have XML generated with encoding 
        /// of the template. Using StringWriter would be a lot simpler here, but would force 
        /// UTF-16 encoding and unnecessarily increase the generated file size.
        /// </remarks>
        public override string TransformText()
        {
            EntityStoreSchemaGenerator generator = new EntityStoreSchemaGenerator(
                this.Provider, 
                this.ConnectionString, 
                this.Namespace + ".Store");

            IList<EdmSchemaError> errors = generator.GenerateStoreMetadata(this.filters);

            EntityFrameworkGenerator.HandleErrors(errors, this.Errors);
            if (this.Errors.HasErrors == false)
            {
                this.WriteXml(generator.WriteStoreSchema);
            }

            this.storageModel = this.GenerationEnvironment.ToString();
            return this.storageModel;
        }

        /// <summary>
        /// Validates properties before code generation.
        /// </summary>
        protected override void Validate()
        {
            if (string.IsNullOrEmpty(this.Provider))
            {
                throw new TransformationException("Provider property must be assigned");
            }

            if (string.IsNullOrEmpty(this.ConnectionString))
            {
                throw new TransformationException("ConnectionString property must be assigned");
            }

            if (string.IsNullOrEmpty(this.Namespace))
            {
                throw new TransformationException("Namespace property must be assigned");
            }
        }
    }
}
